package com.cf.performance.core;

import com.cf.constant.EnumReturnCode;
import com.cf.exception.BizException;
import com.cf.util.ReflectionUtil;
import com.github.kristofa.brave.*;
import com.twitter.zipkin.gen.Span;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;

import javax.annotation.PostConstruct;
import java.util.Random;

/**
 * zipkin-ZLogAspect
 *
 * @author <a href="2428922347@qq.com">Zhu mingming</a>
 * @info
 * @since 2018/3/29 12:20
 */
@Aspect
@Slf4j
@Order(-1111)
public class ZLogAspect {

    @Autowired(required = false)
    private Brave brave;

    private Random random = new Random();

    private ClientSpanState clientState;

    private ServerSpanState serverState;

    @PostConstruct
    public void init() {
        if (brave == null)
            return;
        ClientTracer clientTracer = brave.clientTracer();
        ServerTracer serverTracer = brave.serverTracer();
        clientState = (ClientSpanState) ReflectionUtil.invokeMethodByName(ReflectionUtil.invokeMethodByName(clientTracer, "spanAndEndpoint", new Object[0]), "state", new Object[0]);
        serverState = (ServerSpanState) ReflectionUtil.invokeMethodByName(ReflectionUtil.invokeMethodByName(serverTracer, "spanAndEndpoint", new Object[0]), "state", new Object[0]);
    }

    /**
     * 方法执行时
     */
    @Pointcut("@annotation(Performance)")
    public void ZLogAspect() {
    }

    @Around("@annotation(Performance)")
    public Object beforeTest(ProceedingJoinPoint pj) throws Throwable {
        Span span = handleBefore(pj);
        long startTime = System.currentTimeMillis();
        try {
            return pj.proceed();
        } finally {
            handleAfter(pj, startTime, span);
        }
    }

    private Span handleBefore(ProceedingJoinPoint pj) {
        if (brave == null)
            return null;
        Span parentSpan = CallTrack.last();
        Span currentSpan;
        if (parentSpan == null) {
            currentSpan = SpanId.builder().traceId(random.nextLong()).parentId(random.nextLong()).spanId(random.nextLong()).sampled(true).build().toSpan();
        } else {
            currentSpan = SpanId.builder().traceId(parentSpan.getTrace_id()).parentId(parentSpan.getId()).spanId(random.nextLong()).sampled(true).build().toSpan();
        }
        currentSpan.setName(getCallMethod(pj));
        currentSpan.setTimestamp(System.currentTimeMillis());
        //设置span已经开始
        clientState.setCurrentClientSpan(currentSpan);
        brave.clientTracer().setClientSent();
        CallTrack.push(currentSpan);
        return currentSpan;
    }

    private void handleAfter(ProceedingJoinPoint pj, Long start, Span span) {
        if (brave == null)
            return;
        ClientTracer clientTracer = brave.clientTracer();

        //设置span结束
        clientState.setCurrentClientSpan(span);
        Span pop = CallTrack.pop();
        if (!pop.equals(span)) {
            throw new BizException(EnumReturnCode.ERROR.getCode(), "不应该存在不相等的情况，说明这边的逻辑有问题！");
        }
        clientTracer.setClientReceived();
    }

    private String getCallMethod(ProceedingJoinPoint pj) {
        return pj.getTarget().getClass().getName() + "-" + pj.getSignature().getName();
    }

}
